
// https://www.zhihu.com/question/24401191

/*
    分类大致分为三类：自定义注解、JDK内置注解、还有第三方框架提供的注解。
        自定义注解就是我们自己写的注解，比如@UserLogJDK
        内置注解，比如@Override检验方法重写，@Deprecated标识方法过期等
        第三方框架定义的注解比如SpringMVC的@Controller等

    使用位置实际开发中，注解常常出现在类、方法、成员变量、形参位置。

    如果说注释是写给人看的，那么注解就是写给程序看的。
        它更像一个标签，贴在一个类、一个方法或者字段上。
        它的目的是为当前读取该注解的程序提供判断依据及少量附加信息。
            比如程序只要读到加了@Test的方法，就知道该方法是待测试方法，
            又比如@Before注解，程序看到这个注解，就知道该方法要放在@Test方法之前执行。

    有时我们还可以通过注解属性，为将来读取这个注解的程序提供必要的附加信息，
        比如@RequestMapping("/user/info")提供了Controller某个接口的URL路径。

    级别注解和类、接口、枚举是同一级别的。

    注解的本质@interface和interface从名字上看非常相似，注解的本质是一个接口

    只要用到注解，必然有三角关系：
        定义注解
        使用注解
        读取注解
*/

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

// 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface TimeStampLogPrint {
    boolean resetBase() default false;
    boolean showThreadName() default false;
    String logPre() default "";
    TimeStampScale timeScale() default TimeStampScale.Ms;
}

// 使用注解
class AnnotationTest {
    @TimeStampLogPrint
    public static void run1() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    @TimeStampLogPrint(logPre = "run2", timeScale = TimeStampScale.Us)
    public static void run2() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @TimeStampLogPrint(resetBase = true, showThreadName = true, logPre = "run3", timeScale = TimeStampScale.Ns)
    public static void run3() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

// 读取注解
public class AnnotationDemo {
    protected static void demoLogic() {
        DateTimeDemo.logTs("AnnotationDemo");
        Class clazz = AnnotationTest.class;
        // 通过反射拿到 方法
        Method[] methods = clazz.getDeclaredMethods();
        for (Method runMethod : methods) {
            // // 通过反射拿到 方法上的 TimeStampLogPrint 注解信息
            System.out.println("runMethod:"+runMethod);
            TimeStampLogPrint tsAnno = runMethod.getAnnotation(TimeStampLogPrint.class);

            // 根据注解对象附加的信息，做时间戳设置和调用
            if (tsAnno.resetBase()) {
                DateTimeDemo.resetTsBase();
            }

            DateTimeDemo.logTs(tsAnno.logPre()+", begin", tsAnno.timeScale(), tsAnno.showThreadName());
            try {
                // 通过反射拿到 类方法 invoke调用
                runMethod.invoke(clazz);

            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }
            DateTimeDemo.logTs(tsAnno.logPre()+", end", tsAnno.timeScale(), tsAnno.showThreadName());
        }
    }
}
